package net.yihang.miaosha.controller;

import net.yihang.miaosha.controller.exception.AccessException;
import net.yihang.miaosha.domain.MiaoshaUser;
import net.yihang.miaosha.domain.MiaoshaUserKey;
import net.yihang.miaosha.service.RedisService;
import net.yihang.miaosha.util.CookieUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.http.CacheControl;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static net.yihang.miaosha.controller.LoginController.LOGIN_TOKEN_NAME;

@Configuration
public class ControllerConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private RedisService redisService;
    private final String LOGIN_USER = "login_user";

    // <mvc:resources mapping="浏览器路径" location="磁盘路径"/>
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        CacheControl cacheControl = CacheControl.maxAge(360, TimeUnit.SECONDS);
        // 处理静态资源路径映射的
        registry.addResourceHandler("/html/**").addResourceLocations("file:e:\\html\\")
                .setCacheControl(cacheControl);
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new HandlerMethodArgumentResolver() {
            @Override
            public boolean supportsParameter(MethodParameter parameter) {
                // 只有参数类型为MiaoshaUser 时，才应用这个解析器
                return parameter.getParameterType() == MiaoshaUser.class;
            }

            @Override
            public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
                return webRequest.getAttribute(LOGIN_USER, RequestAttributes.SCOPE_REQUEST);
            }
        });
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HandlerInterceptorAdapter() {
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                String token = request.getParameter(LOGIN_TOKEN_NAME);
                Cookie[] cookies = request.getCookies();
                if (token == null && cookies != null) {
                    Optional<Cookie> first = Arrays.stream(cookies).filter((cookie) -> cookie.getName().equals(LOGIN_TOKEN_NAME)).findFirst();
                    if (first.isPresent()) {
                        token = first.get().getValue();
                    }
                }
                if (token == null) {
                    throw new AccessException("访问被拒绝");// , 不能直接用response跳转也不能抛异常
                }
                MiaoshaUserKey key = MiaoshaUserKey.fromToken(token);
                MiaoshaUser miaoshaUser = redisService.get(key, MiaoshaUser.class);
                if (miaoshaUser == null) {
                    throw new AccessException("访问被拒绝(登录已过期)");
                }
                // 更新缓存过期时间
                redisService.set(key, miaoshaUser);
                // 更新cookie过期时间
                CookieUtil.updateTokenCookie(response, key);
                request.setAttribute(LOGIN_USER, miaoshaUser);
                return true;
            }
        }).addPathPatterns("/goods/**");
    }
}
